1 module hip.jni.android.android_native_app_glue;
2 version(Android):
3 /*
4  * Copyright (C) 2010 The Android Open Source Project
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19 
20 import core.runtime : rt_init, rt_term;
21 import core.stdc.errno : errno;
22 import core.stdc.stdarg : va_list, va_start;
23 import core.stdc.stdlib : free, malloc;
24 import core.stdc.string : memcpy, memset, strerror;
25 import core.sys.posix.pthread, core.sys.posix.unistd : close, pipe, read, write;
26 
27 import hip.jni.android.input, hip.jni.android.native_window, hip.jni.android.rect : ARect;
28 import hip.jni.android.configuration, hip.jni.android.log, hip.jni.android.looper, hip.jni.android.native_activity;
29 
30 version (Android) @system:
31 
32 /**
33  * The native activity interface provided by <android/native_activity.h>
34  * is based on a set of application-provided callbacks that will be called
35  * by the Activity's main thread when certain events occur.
36  *
37  * This means that each one of this callbacks _should_ _not_ block, or they
38  * risk having the system force-close the application. This programming
39  * model is direct, lightweight, but constraining.
40  *
41  * The 'android_native_app_glue' static library is used to provide a different
42  * execution model where the application can implement its own main event
43  * loop in a different thread instead. Here's how it works:
44  *
45  * 1/ The application must provide a function named "android_main()" that
46  *    will be called when the activity is created, in a new thread that is
47  *    distinct from the activity's main thread.
48  *
49  * 2/ android_main() receives a pointer to a valid "android_app" structure
50  *    that contains references to other important objects, e.g. the
51  *    ANativeActivity obejct instance the application is running in.
52  *
53  * 3/ the "android_app" object holds an ALooper instance that already
54  *    listens to two important things:
55  *
56  *      - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX
57  *        declarations below.
58  *
59  *      - input events coming from the AInputQueue attached to the activity.
60  *
61  *    Each of these correspond to an ALooper identifier returned by
62  *    ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT,
63  *    respectively.
64  *
65  *    Your application can use the same ALooper to listen to additional
66  *    file-descriptors.  They can either be callback based, or with return
67  *    identifiers starting with LOOPER_ID_USER.
68  *
69  * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event,
70  *    the returned data will point to an android_poll_source structure.  You
71  *    can call the process() function on it, and fill in android_app.onAppCmd
72  *    and android_app.onInputEvent to be called for your own processing
73  *    of the event.
74  *
75  *    Alternatively, you can call the low-level functions to read and process
76  *    the data directly...  look at the process_cmd() and process_input()
77  *    implementations in the glue to see how to do this.
78  *
79  * See the sample named "native-activity" that comes with the NDK with a
80  * full usage example.  Also look at the JavaDoc of NativeActivity.
81  */
82 
83 /**
84  * Data associated with an ALooper fd that will be returned as the "outData"
85  * when that source has data ready.
86  */
87 struct android_poll_source {
88     // The identifier of this source.  May be LOOPER_ID_MAIN or
89     // LOOPER_ID_INPUT.
90     int id;
91 
92     // The android_app this ident is associated with.
93     android_app* app;
94 
95     // Function to call to perform the standard processing of data from
96     // this source.
97     void function(android_app*, android_poll_source*) process;
98 }
99 
100 /**
101  * This is the interface for the standard glue code of a threaded
102  * application.  In this model, the application's code is running
103  * in its own thread separate from the main thread of the process.
104  * It is not required that this thread be associated with the Java
105  * VM, although it will need to be in order to make JNI calls any
106  * Java objects.
107  */
108 struct android_app {
109     // The application can place a pointer to its own state object
110     // here if it likes.
111     void* userData;
112 
113     // Fill this in with the function to process main app commands (APP_CMD_*)
114     void function(android_app*, int) onAppCmd;
115 
116     // Fill this in with the function to process input events.  At this point
117     // the event has already been pre-dispatched, and it will be finished upon
118     // return.  Return 1 if you have handled the event, 0 for any default
119     // dispatching.
120     int function(android_app*, AInputEvent*) onInputEvent;
121 
122     // The ANativeActivity object instance that this app is running in.
123     ANativeActivity* activity;
124 
125     // The current configuration the app is running in.
126     AConfiguration* config;
127 
128     // This is the last instance's saved state, as provided at creation time.
129     // It is null if there was no state.  You can use this as you need; the
130     // memory will remain around until you call android_app_exec_cmd() for
131     // APP_CMD_RESUME, at which point it will be freed and savedState set to null.
132     // These variables should only be changed when processing a APP_CMD_SAVE_STATE,
133     // at which point they will be initialized to null and you can malloc your
134     // state and place the information here.  In that case the memory will be
135     // freed for you later.
136     void* savedState;
137     size_t savedStateSize;
138 
139     // The ALooper associated with the app's thread.
140     ALooper* looper;
141 
142     // When non-null, this is the input queue from which the app will
143     // receive user input events.
144     AInputQueue* inputQueue;
145 
146     // When non-null, this is the window surface that the app can draw in.
147     ANativeWindow* window;
148 
149     // Current content rectangle of the window; this is the area where the
150     // window's content should be placed to be seen by the user.
151     ARect contentRect;
152 
153     // Current state of the app's activity.  May be either APP_CMD_START,
154     // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below.
155     int activityState;
156 
157     // This is non-zero when the application's NativeActivity is being
158     // destroyed and waiting for the app thread to complete.
159     int destroyRequested;
160 
161     // -------------------------------------------------
162     // Below are "private" implementation of the glue code.
163 
164     pthread_mutex_t mutex;
165     pthread_cond_t cond;
166 
167     int msgread;
168     int msgwrite;
169 
170     pthread_t thread;
171 
172     android_poll_source cmdPollSource;
173     android_poll_source inputPollSource;
174 
175     int running;
176     int stateSaved;
177     int destroyed;
178     int redrawNeeded;
179     AInputQueue* pendingInputQueue;
180     ANativeWindow* pendingWindow;
181     ARect pendingContentRect;
182 }
183 
184 enum {
185     /**
186      * Looper data ID of commands coming from the app's main thread, which
187      * is returned as an identifier from ALooper_pollOnce().  The data for this
188      * identifier is a pointer to an android_poll_source structure.
189      * These can be retrieved and processed with android_app_read_cmd()
190      * and android_app_exec_cmd().
191      */
192     LOOPER_ID_MAIN = 1,
193 
194     /**
195      * Looper data ID of events coming from the AInputQueue of the
196      * application's window, which is returned as an identifier from
197      * ALooper_pollOnce().  The data for this identifier is a pointer to an
198      * android_poll_source structure.  These can be read via the inputQueue
199      * object of android_app.
200      */
201     LOOPER_ID_INPUT = 2,
202 
203     /**
204      * Start of user-defined ALooper identifiers.
205      */
206     LOOPER_ID_USER = 3,
207 }
208 
209 enum {
210     /**
211      * Command from main thread: the AInputQueue has changed.  Upon processing
212      * this command, android_app.inputQueue will be updated to the new queue
213      * (or null).
214      */
215     APP_CMD_INPUT_CHANGED,
216 
217     /**
218      * Command from main thread: a new ANativeWindow is ready for use.  Upon
219      * receiving this command, android_app.window will contain the new window
220      * surface.
221      */
222     APP_CMD_INIT_WINDOW,
223 
224     /**
225      * Command from main thread: the existing ANativeWindow needs to be
226      * terminated.  Upon receiving this command, android_app.window still
227      * contains the existing window; after calling android_app_exec_cmd
228      * it will be set to null.
229      */
230     APP_CMD_TERM_WINDOW,
231 
232     /**
233      * Command from main thread: the current ANativeWindow has been resized.
234      * Please redraw with its new size.
235      */
236     APP_CMD_WINDOW_RESIZED,
237 
238     /**
239      * Command from main thread: the system needs that the current ANativeWindow
240      * be redrawn.  You should redraw the window before handing this to
241      * android_app_exec_cmd() in order to avoid transient drawing glitches.
242      */
243     APP_CMD_WINDOW_REDRAW_NEEDED,
244 
245     /**
246      * Command from main thread: the content area of the window has changed,
247      * such as from the soft input window being shown or hidden.  You can
248      * find the new content rect in android_app::contentRect.
249      */
250     APP_CMD_CONTENT_RECT_CHANGED,
251 
252     /**
253      * Command from main thread: the app's activity window has gained
254      * input focus.
255      */
256     APP_CMD_GAINED_FOCUS,
257 
258     /**
259      * Command from main thread: the app's activity window has lost
260      * input focus.
261      */
262     APP_CMD_LOST_FOCUS,
263 
264     /**
265      * Command from main thread: the current device configuration has changed.
266      */
267     APP_CMD_CONFIG_CHANGED,
268 
269     /**
270      * Command from main thread: the system is running low on memory.
271      * Try to reduce your memory use.
272      */
273     APP_CMD_LOW_MEMORY,
274 
275     /**
276      * Command from main thread: the app's activity has been started.
277      */
278     APP_CMD_START,
279 
280     /**
281      * Command from main thread: the app's activity has been resumed.
282      */
283     APP_CMD_RESUME,
284 
285     /**
286      * Command from main thread: the app should generate a new saved state
287      * for itself, to restore from later if needed.  If you have saved state,
288      * allocate it with malloc and place it in android_app.savedState with
289      * the size in android_app.savedStateSize.  The will be freed for you
290      * later.
291      */
292     APP_CMD_SAVE_STATE,
293 
294     /**
295      * Command from main thread: the app's activity has been paused.
296      */
297     APP_CMD_PAUSE,
298 
299     /**
300      * Command from main thread: the app's activity has been stopped.
301      */
302     APP_CMD_STOP,
303 
304     /**
305      * Command from main thread: the app's activity is being destroyed,
306      * and waiting for the app thread to clean up and exit before proceeding.
307      */
308     APP_CMD_DESTROY,
309 }
310 
311 /**
312  * This is the function that application code must implement, representing
313  * the main entry to the app.
314  */
315 extern(C) void android_main(android_app* app);
316 
317 private:
318 int LOGI(const(char)* warning) { return __android_log_print(android_LogPriority.ANDROID_LOG_INFO, "threaded_app", warning); }
319 int LOGE(const(char)* fmt, ...) {
320     va_list arg_list;
321     va_start(arg_list, fmt);
322     return __android_log_print(android_LogPriority.ANDROID_LOG_ERROR, "threaded_app", fmt, arg_list);
323 }
324 
325 /* For debug builds, always enable the debug traces in this library */
326 int LOGV(const(char)* fmt, ...) {
327     debug {
328         va_list arg_list;
329         va_start(arg_list, fmt);
330         return __android_log_print(android_LogPriority.ANDROID_LOG_VERBOSE, "threaded_app", fmt, arg_list);
331     } else
332         return 0;
333 }
334 
335 void free_saved_state(android_app* android_app) {
336     pthread_mutex_lock(&android_app.mutex);
337     if (android_app.savedState != null) {
338         free(android_app.savedState);
339         android_app.savedState = null;
340         android_app.savedStateSize = 0;
341     }
342     pthread_mutex_unlock(&android_app.mutex);
343 }
344 
345 void print_cur_config(android_app* android_app) {
346     char[2] lang, country;
347     AConfiguration_getLanguage(android_app.config, lang.ptr);
348     AConfiguration_getCountry(android_app.config, country.ptr);
349 
350     LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
351           ~ "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
352           ~ "modetype=%d modenight=%d",
353             AConfiguration_getMcc(android_app.config),
354             AConfiguration_getMnc(android_app.config),
355             lang[0], lang[1], country[0], country[1],
356             AConfiguration_getOrientation(android_app.config),
357             AConfiguration_getTouchscreen(android_app.config),
358             AConfiguration_getDensity(android_app.config),
359             AConfiguration_getKeyboard(android_app.config),
360             AConfiguration_getNavigation(android_app.config),
361             AConfiguration_getKeysHidden(android_app.config),
362             AConfiguration_getNavHidden(android_app.config),
363             AConfiguration_getSdkVersion(android_app.config),
364             AConfiguration_getScreenSize(android_app.config),
365             AConfiguration_getScreenLong(android_app.config),
366             AConfiguration_getUiModeType(android_app.config),
367             AConfiguration_getUiModeNight(android_app.config));
368 }
369 
370 public:
371 /**
372  * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next
373  * app command message.
374  */
375 byte android_app_read_cmd(android_app* android_app) {
376     byte cmd;
377     if (read(android_app.msgread, &cmd, cmd.sizeof) == cmd.sizeof) {
378         switch (cmd) {
379             case APP_CMD_SAVE_STATE:
380                 free_saved_state(android_app);
381                 break;
382             default:
383                 break;
384         }
385         return cmd;
386     } else {
387         LOGE("No data on command pipe!");
388     }
389     return -1;
390 }
391 
392 /**
393  * Call with the command returned by android_app_read_cmd() to do the
394  * initial pre-processing of the given command.  You can perform your own
395  * actions for the command after calling this function.
396  */
397 void android_app_pre_exec_cmd(android_app* android_app, byte cmd) {
398     switch (cmd) {
399         case APP_CMD_INPUT_CHANGED:
400             LOGV("APP_CMD_INPUT_CHANGED\n");
401             pthread_mutex_lock(&android_app.mutex);
402             if (android_app.inputQueue != null) {
403                 AInputQueue_detachLooper(android_app.inputQueue);
404             }
405             android_app.inputQueue = android_app.pendingInputQueue;
406             if (android_app.inputQueue != null) {
407                 LOGV("Attaching input queue to looper");
408                 AInputQueue_attachLooper(android_app.inputQueue,
409                         android_app.looper, LOOPER_ID_INPUT, null,
410                         &android_app.inputPollSource);
411             }
412             pthread_cond_broadcast(&android_app.cond);
413             pthread_mutex_unlock(&android_app.mutex);
414             break;
415 
416         case APP_CMD_INIT_WINDOW:
417             LOGV("APP_CMD_INIT_WINDOW\n");
418             pthread_mutex_lock(&android_app.mutex);
419             android_app.window = android_app.pendingWindow;
420             pthread_cond_broadcast(&android_app.cond);
421             pthread_mutex_unlock(&android_app.mutex);
422             break;
423 
424         case APP_CMD_TERM_WINDOW:
425             LOGV("APP_CMD_TERM_WINDOW\n");
426             pthread_cond_broadcast(&android_app.cond);
427             break;
428 
429         case APP_CMD_RESUME:
430         case APP_CMD_START:
431         case APP_CMD_PAUSE:
432         case APP_CMD_STOP:
433             LOGV("activityState=%d\n", cmd);
434             pthread_mutex_lock(&android_app.mutex);
435             android_app.activityState = cmd;
436             pthread_cond_broadcast(&android_app.cond);
437             pthread_mutex_unlock(&android_app.mutex);
438             break;
439 
440         case APP_CMD_CONFIG_CHANGED:
441             LOGV("APP_CMD_CONFIG_CHANGED\n");
442             AConfiguration_fromAssetManager(android_app.config,
443                     android_app.activity.assetManager);
444             print_cur_config(android_app);
445             break;
446 
447         case APP_CMD_DESTROY:
448             LOGV("APP_CMD_DESTROY\n");
449             android_app.destroyRequested = 1;
450             break;
451         default:
452             break;
453     }
454 }
455 
456 /**
457  * Call with the command returned by android_app_read_cmd() to do the
458  * final post-processing of the given command.  You must have done your own
459  * actions for the command before calling this function.
460  */
461 void android_app_post_exec_cmd(android_app* android_app, byte cmd) {
462     switch (cmd) {
463         case APP_CMD_TERM_WINDOW:
464             LOGV("APP_CMD_TERM_WINDOW\n");
465             pthread_mutex_lock(&android_app.mutex);
466             android_app.window = null;
467             pthread_cond_broadcast(&android_app.cond);
468             pthread_mutex_unlock(&android_app.mutex);
469             break;
470 
471         case APP_CMD_SAVE_STATE:
472             LOGV("APP_CMD_SAVE_STATE\n");
473             pthread_mutex_lock(&android_app.mutex);
474             android_app.stateSaved = 1;
475             pthread_cond_broadcast(&android_app.cond);
476             pthread_mutex_unlock(&android_app.mutex);
477             break;
478 
479         case APP_CMD_RESUME:
480             free_saved_state(android_app);
481             break;
482         default:
483             break;
484     }
485 }
486 
487 /**
488  * Dummy function you can call to ensure glue code isn't stripped.
489  */
490 void app_dummy() {
491 
492 }
493 
494 private:
495 void android_app_destroy(android_app* android_app) {
496     LOGV("android_app_destroy!");
497     free_saved_state(android_app);
498     pthread_mutex_lock(&android_app.mutex);
499     if (android_app.inputQueue != null) {
500         AInputQueue_detachLooper(android_app.inputQueue);
501     }
502     AConfiguration_delete(android_app.config);
503     android_app.destroyed = 1;
504     pthread_cond_broadcast(&android_app.cond);
505     pthread_mutex_unlock(&android_app.mutex);
506     // Can't touch android_app object after this.
507 }
508 
509 void process_input(android_app* app, android_poll_source* source) {
510     AInputEvent* event = null;
511     while (AInputQueue_getEvent(app.inputQueue, &event) >= 0) {
512         LOGV("New input event: type=%d\n", AInputEvent_getType(event));
513         if (AInputQueue_preDispatchEvent(app.inputQueue, event)) {
514             continue;
515         }
516         int handled = 0;
517         if (app.onInputEvent != null) handled = app.onInputEvent(app, event);
518         AInputQueue_finishEvent(app.inputQueue, event, handled);
519     }
520 }
521 
522 void process_cmd(android_app* app, android_poll_source* source) {
523     byte cmd = android_app_read_cmd(app);
524     android_app_pre_exec_cmd(app, cmd);
525     if (app.onAppCmd != null) app.onAppCmd(app, cmd);
526     android_app_post_exec_cmd(app, cmd);
527 }
528 
529 extern(C) void* android_app_entry(void* param) {
530     android_app* android_app = cast(android_app*)param;
531 
532     android_app.config = AConfiguration_new();
533     AConfiguration_fromAssetManager(android_app.config, android_app.activity.assetManager);
534 
535     print_cur_config(android_app);
536 
537     android_app.cmdPollSource.id = LOOPER_ID_MAIN;
538     android_app.cmdPollSource.app = android_app;
539     android_app.cmdPollSource.process = &process_cmd;
540     android_app.inputPollSource.id = LOOPER_ID_INPUT;
541     android_app.inputPollSource.app = android_app;
542     android_app.inputPollSource.process = &process_input;
543 
544     ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
545     ALooper_addFd(looper, android_app.msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, null,
546             &android_app.cmdPollSource);
547     android_app.looper = looper;
548 
549     pthread_mutex_lock(&android_app.mutex);
550     android_app.running = 1;
551     pthread_cond_broadcast(&android_app.cond);
552     pthread_mutex_unlock(&android_app.mutex);
553 
554     rt_init();
555     android_main(android_app);
556     rt_term();
557 
558     android_app_destroy(android_app);
559     return null;
560 }
561 
562 // --------------------------------------------------------------------
563 // Native activity interaction (called from main thread)
564 // --------------------------------------------------------------------
565 
566 android_app* android_app_create(ANativeActivity* activity,
567         void* savedState, size_t savedStateSize) {
568     android_app* andro_app = cast(android_app*)malloc(android_app.sizeof);
569     memset(andro_app, 0, android_app.sizeof);
570     andro_app.activity = activity;
571 
572     pthread_mutex_init(&andro_app.mutex, null);
573     pthread_cond_init(&andro_app.cond, null);
574 
575     if (savedState != null) {
576         andro_app.savedState = malloc(savedStateSize);
577         andro_app.savedStateSize = savedStateSize;
578         memcpy(andro_app.savedState, savedState, savedStateSize);
579     }
580 
581     int[2] msgpipe;
582     if (pipe(msgpipe)) {
583         LOGE("could not create pipe: %s", strerror(errno));
584         return null;
585     }
586     andro_app.msgread = msgpipe[0];
587     andro_app.msgwrite = msgpipe[1];
588 
589     pthread_attr_t attr;
590     pthread_attr_init(&attr);
591     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
592     pthread_create(&andro_app.thread, &attr, &android_app_entry, andro_app);
593 
594     // Wait for thread to start.
595     pthread_mutex_lock(&andro_app.mutex);
596     while (!andro_app.running) {
597         pthread_cond_wait(&andro_app.cond, &andro_app.mutex);
598     }
599     pthread_mutex_unlock(&andro_app.mutex);
600 
601     return andro_app;
602 }
603 
604 void android_app_write_cmd(android_app* android_app, byte cmd) {
605     if (write(android_app.msgwrite, &cmd, cmd.sizeof) != cmd.sizeof) {
606         LOGE("Failure writing android_app cmd: %s\n", strerror(errno));
607     }
608 }
609 
610 void android_app_set_input(android_app* android_app, AInputQueue* inputQueue) {
611     pthread_mutex_lock(&android_app.mutex);
612     android_app.pendingInputQueue = inputQueue;
613     android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED);
614     while (android_app.inputQueue != android_app.pendingInputQueue) {
615         pthread_cond_wait(&android_app.cond, &android_app.mutex);
616     }
617     pthread_mutex_unlock(&android_app.mutex);
618 }
619 
620 void android_app_set_window(android_app* android_app, ANativeWindow* window) {
621     pthread_mutex_lock(&android_app.mutex);
622     if (android_app.pendingWindow != null) {
623         android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
624     }
625     android_app.pendingWindow = window;
626     if (window != null) {
627         android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW);
628     }
629     while (android_app.window != android_app.pendingWindow) {
630         pthread_cond_wait(&android_app.cond, &android_app.mutex);
631     }
632     pthread_mutex_unlock(&android_app.mutex);
633 }
634 
635 void android_app_set_activity_state(android_app* android_app, byte cmd) {
636     pthread_mutex_lock(&android_app.mutex);
637     android_app_write_cmd(android_app, cmd);
638     while (android_app.activityState != cmd) {
639         pthread_cond_wait(&android_app.cond, &android_app.mutex);
640     }
641     pthread_mutex_unlock(&android_app.mutex);
642 }
643 
644 void android_app_free(android_app* android_app) {
645     pthread_mutex_lock(&android_app.mutex);
646     android_app_write_cmd(android_app, APP_CMD_DESTROY);
647     while (!android_app.destroyed) {
648         pthread_cond_wait(&android_app.cond, &android_app.mutex);
649     }
650     pthread_mutex_unlock(&android_app.mutex);
651 
652     close(android_app.msgread);
653     close(android_app.msgwrite);
654     pthread_cond_destroy(&android_app.cond);
655     pthread_mutex_destroy(&android_app.mutex);
656     free(android_app);
657 }
658 
659 extern(C) void onDestroy(ANativeActivity* activity) {
660     LOGV("Destroy: %p\n", activity);
661     android_app_free(cast(android_app*)activity.instance);
662 }
663 
664 extern(C) void onStart(ANativeActivity* activity) {
665     LOGV("Start: %p\n", activity);
666     android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_START);
667 }
668 
669 extern(C) void onResume(ANativeActivity* activity) {
670     LOGV("Resume: %p\n", activity);
671     android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_RESUME);
672 }
673 
674 extern(C) void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
675     android_app* android_app = cast(android_app*)activity.instance;
676     void* savedState = null;
677 
678     LOGV("SaveInstanceState: %p\n", activity);
679     pthread_mutex_lock(&android_app.mutex);
680     android_app.stateSaved = 0;
681     android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
682     while (!android_app.stateSaved) {
683         pthread_cond_wait(&android_app.cond, &android_app.mutex);
684     }
685 
686     if (android_app.savedState != null) {
687         savedState = android_app.savedState;
688         *outLen = android_app.savedStateSize;
689         android_app.savedState = null;
690         android_app.savedStateSize = 0;
691     }
692 
693     pthread_mutex_unlock(&android_app.mutex);
694 
695     return savedState;
696 }
697 
698 extern(C) void onPause(ANativeActivity* activity) {
699     LOGV("Pause: %p\n", activity);
700     android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_PAUSE);
701 }
702 
703 extern(C) void onStop(ANativeActivity* activity) {
704     LOGV("Stop: %p\n", activity);
705     android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_STOP);
706 }
707 
708 extern(C) void onConfigurationChanged(ANativeActivity* activity) {
709     android_app* android_app = cast(android_app*)activity.instance;
710     LOGV("ConfigurationChanged: %p\n", activity);
711     android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED);
712 }
713 
714 extern(C) void onLowMemory(ANativeActivity* activity) {
715     android_app* android_app = cast(android_app*)activity.instance;
716     LOGV("LowMemory: %p\n", activity);
717     android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY);
718 }
719 
720 extern(C) void onWindowFocusChanged(ANativeActivity* activity, int focused) {
721     LOGV("WindowFocusChanged: %p -- %d\n", activity, focused);
722     android_app_write_cmd(cast(android_app*)activity.instance,
723             focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
724 }
725 
726 extern(C) void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
727     LOGV("NativeWindowCreated: %p -- %p\n", activity, window);
728     android_app_set_window(cast(android_app*)activity.instance, window);
729 }
730 
731 extern(C) void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
732     LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window);
733     android_app_set_window(cast(android_app*)activity.instance, null);
734 }
735 
736 extern(C) void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
737     LOGV("InputQueueCreated: %p -- %p\n", activity, queue);
738     android_app_set_input(cast(android_app*)activity.instance, queue);
739 }
740 
741 extern(C) void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
742     LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue);
743     android_app_set_input(cast(android_app*)activity.instance, null);
744 }
745 
746 public:
747 extern(C) void ANativeActivity_onCreate(ANativeActivity* activity,
748         void* savedState, size_t savedStateSize) {
749     LOGV("Creating: %p\n", activity);
750     activity.callbacks.onDestroy = &onDestroy;
751     activity.callbacks.onStart = &onStart;
752     activity.callbacks.onResume = &onResume;
753     activity.callbacks.onSaveInstanceState = &onSaveInstanceState;
754     activity.callbacks.onPause = &onPause;
755     activity.callbacks.onStop = &onStop;
756     activity.callbacks.onConfigurationChanged = &onConfigurationChanged;
757     activity.callbacks.onLowMemory = &onLowMemory;
758     activity.callbacks.onWindowFocusChanged = &onWindowFocusChanged;
759     activity.callbacks.onNativeWindowCreated = &onNativeWindowCreated;
760     activity.callbacks.onNativeWindowDestroyed = &onNativeWindowDestroyed;
761     activity.callbacks.onInputQueueCreated = &onInputQueueCreated;
762     activity.callbacks.onInputQueueDestroyed = &onInputQueueDestroyed;
763 
764     activity.instance = android_app_create(activity, savedState, savedStateSize);
765 }